#ifndef MSC_STR_H
#define MSC_STR_H

#include "mem_base.h"
#include <stdarg.h>

#define MS_STRING_STEP  16

typedef struct ms_string_s {
    ms_alloc_t* ac; //内存管理工具
    size_t step;    //最小增量
    size_t count;   //当前字符个数
    size_t max_count;   //当前内存最大存储个数
    char* ptr;    //当前元素首地址
} ms_string_t, *ms_string;

static inline void ms_string_reallocate(ms_string s, size_t size) {
    if (s->ptr) {
        void* newp = ms_reallocate(s->ac, s->ptr, size);
        if (newp) {
            s->ptr = (char*)newp;
			if (size > s->max_count) {
				memset(s->ptr + s->max_count, 0, size - s->max_count);
			}
			s->max_count = size;
        }
    }
    else {
        s->ptr = (char*)ms_allocate(s->ac, size);
        if (s->ptr) {
            s->max_count = size;
			memset(s->ptr, 0, size);
        }
    }
}

static inline void ms_string_grow(ms_string s, size_t no_less_than) {
    size_t new_count = 0;
    if (0 == no_less_than) {
        new_count = (s->max_count+s->step)/s->step*s->step;
    }
    else if (no_less_than > s->max_count) {
        new_count = (no_less_than+s->step)/s->step*s->step;
    }
    if (new_count > s->max_count) {
        ms_string_reallocate(s, new_count);
    }
}

static inline void ms_string_shrink(ms_string s) {
    if (s->count < s->max_count / 2 && s->count > s->step) {
        size_t new_count = (s->count+s->step)/s->step*s->step;
        ms_string_reallocate(s, new_count);
    }
}

CPUBLIC char* ms_string_assign(ms_string s, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (0 == len) {
        len = strlen(str);
    }
    if (s->max_count < len) {
        ms_string_grow(s, len+1);//最后需要一个结束符'\0'
    }
    if (len < s->max_count) {
        memcpy(s->ptr, str, len);
        s->count = len;
        return s->ptr;
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_assign_c(ms_string s, size_t count, char c) {
#ifdef _IF_NULL_
    if (s && count > 0) {
#endif
    if (s->max_count < count) {
        ms_string_grow(s, count+1);//最后需要一个结束符'\0'
    }
    if (count < s->max_count) {
        memset(s->ptr, c, count);
        s->count = count;
        return s->ptr;
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC ms_string ms_string_create(ms_alloc_t* ac, size_t step, char* str, size_t len) {
    if (!ac) {
        ac = ms_global_alloc();
    }
    ms_string s = (ms_string)ms_allocate(ac, sizeof(ms_string_t));
    if (s) {
        s->ac = ac;
        if (step)
            s->step = step;
        else
            s->step = MS_STRING_STEP;
        s->count = 0;
        s->max_count = 0;
        s->ptr = NULL;
        if (str) {
            if (!ms_string_assign(s, str, len)) {
                ms_deallocate(ac, s);
                s = NULL;
            }
        }
    }
    return s;
}


CPUBLIC ms_string ms_string_create_c(ms_alloc_t* ac, size_t step, size_t count, char c) {
    if (!ac) {
        ac = ms_global_alloc();
    }
    ms_string s = (ms_string)ms_allocate(ac, sizeof(ms_string_t));
    if (s) {
        s->ac = ac;
        if (step)
            s->step = step;
        else
            s->step = MS_STRING_STEP;
        s->count = 0;
        s->max_count = 0;
        s->ptr = NULL;
        if (count > 0) {
            if (!ms_string_assign_c(s, count, c)) {
                ms_deallocate(ac, s);
                s = NULL;
            }
        }
    }
    return s;
}

CPUBLIC void ms_string_destory(ms_string s) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (s->ptr) {
        ms_deallocate(s->ac, s->ptr);
    }
    ms_deallocate(s->ac, s);
#ifdef _IF_NULL_
    }
#endif
}

CPUBLIC void ms_string_clear(ms_string s) {
    if (s) {
        if (s->max_count > s->step) {
           ms_deallocate(s->ac, s->ptr);
           s->max_count = s->count = 0;
           s->ptr = NULL;
        }
        else {
            memset(s->ptr, 0, s->count);
            s->count = 0;
        }
    }
}


CPUBLIC bool ms_string_empty(ms_string s) {
    return (NULL == s) || (0 == s->count);
}

CPUBLIC size_t ms_string_size(ms_string s) {
#ifdef _IF_NULL_
    if (s) {
#endif
    return s->count;
#ifdef _IF_NULL_
    }
    return 0;
#endif
}

CPUBLIC size_t ms_string_lenght(ms_string s) {
    return ms_string_size(s);
}

CPUBLIC char* ms_string_append(ms_string s, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (0 == len) {
        len = strlen(str);
    }
    if (len > 0) {
        if (s->max_count < s->count+len+1) {
            ms_string_grow(s, s->count+len+1);
        }
        if (s->count+len < s->max_count) {
            memcpy(s->ptr+s->count, str, len);
            s->count += len;
            return s->ptr;
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_append_c(ms_string s, size_t count, char c) {
#ifdef _IF_NULL_
    if (s && count > 0) {
#endif
    if (s->max_count < s->count+count+1) {
        ms_string_grow(s, s->count+count+1);
    }
    if (s->count+count < s->max_count) {
        memset(s->ptr+s->count, c, count);
        s->count += count;
        return s->ptr;
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_insert(ms_string s, size_t off, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (off <= s->count) {
        if (0 == len) {
            len = strlen(str);
        }
        if (len > 0) {
            if (s->max_count < s->count+len+1) {
                ms_string_grow(s, s->count+len+1);
            }
            if (s->count+len < s->max_count) {
                if (off < s->count) {
                    memmove(s->ptr+off+len, s->ptr+off, s->count-off);
                }
                memcpy(s->ptr+off, str, len);
                s->count += len;
                return s->ptr;
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_insert_c(ms_string s, size_t off, size_t count, char c) {
#ifdef _IF_NULL_
    if (s && count > 0) {
#endif
    if (off <= s->count) {
        if (s->max_count < s->count+count+1) {
            ms_string_grow(s, s->count+count+1);
        }
        if (s->count+count < s->max_count) {
            if (off < s->count) {
                memmove(s->ptr+off+count, s->ptr+off, s->count-off);
            }
            memset(s->ptr+off, c, count);
            s->count += count;
            return s->ptr;
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_format(ms_string s, const char* const fmt, ...) {
    char* p = NULL;
#ifdef _IF_NULL_
    if (s) {
#endif
    if (s->count > 0) {
        memset(s->ptr, 0, s->count);
        s->count = 0;
    }
    int ret = -1;
    va_list args;
    va_start(args, fmt);
    ret = vsnprintf(NULL, 0, fmt, args);
    if (ret > 0) {
        if (s->max_count < (size_t)ret+1) {
            ms_string_grow(s, ret +1);
        }
        if ((size_t)ret < s->max_count) {
            ret = vsnprintf(s->ptr, s->max_count, fmt, args);
            if (ret > 0) {
                s->count = ret;
                p = s->ptr;
            }
        }
    }
    va_end(args);
#ifdef _IF_NULL_
    }
#endif
    return p;
}

CPUBLIC char* ms_string_append_format(ms_string s, const char* const fmt, ...) {
    char* p = NULL;
#ifdef _IF_NULL_
    if (s) {
#endif
    int ret = -1;
    va_list args;
    va_start(args, fmt);
    ret = vsnprintf(NULL, 0, fmt, args);
    if (ret > 0) {
        if (s->max_count < (size_t)ret+s->count+1) {
            ms_string_grow(s, ret+s->count +1);
        }
        if ((size_t)ret+s->count < s->max_count) {
            ret = vsnprintf(s->ptr+s->count, s->max_count-s->count, fmt, args);
            if (ret > 0) {
                s->count += ret;
                p = s->ptr;
            }
        }
    }
    va_end(args);
#ifdef _IF_NULL_
    }
#endif
    return p;
}

CPUBLIC char* ms_string_insert_format(ms_string s, size_t off, const char* const fmt, ...) {
    char* p = NULL;
#ifdef _IF_NULL_
    if (s && fmt) {
#endif
    if (off <= s->count) {
        int ret = -1;
        va_list args;
        va_start(args, fmt);
        ret = vsnprintf(NULL, 0, fmt, args);
        if (ret > 0) {
            if (s->max_count < (size_t)ret+s->count+1) {
                ms_string_grow(s, ret+s->count +1);
            }
            if ((size_t)ret+s->count < s->max_count) {
                if (off < s->count) {
                    memmove(s->ptr+off+ret, s->ptr+off, s->count-off);
                }
                int ret2 = vsnprintf(s->ptr+off, s->max_count-off, fmt, args);
                if (ret2 == ret) {
                    s->count += ret;
                    p = s->ptr;
                }
                else {
                    memmove(s->ptr+off, s->ptr+off+ret, s->count-off);
                }
            }
        }
        va_end(args);
    }
#ifdef _IF_NULL_
    }
#endif
    return p;
}

CPUBLIC char* ms_string_replace(ms_string s, size_t off, size_t no, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (off <= s->count) {
        if (off + no > s->count) {
            no = s->count - off;
        }
        if (0 == no) return ms_string_insert(s, off, str, len);
        if (0 == len) {
            len = strlen(str);
        }
        if (len > 0) {
            if (no == len) {
                memcpy(s->ptr+off, str, len);
                return s->ptr;
            }
            else if (no > len) {
                memcpy(s->ptr+off, str, len);
                if (off + no < s->count)
                    memmove(s->ptr+off+len, s->ptr+off+no, s->count-off-no);
                else
                    memset(s->ptr+off+len, 0, s->count - off - len);
                s->count -= no - len;
                return s->ptr;
            }
            else {
                if (s->max_count < s->count + len - no + 1) {
                    ms_string_grow(s, s->count + len - no + 1);
                }
                if (s->count + len - no < s->max_count) {
                    if (off + no < s->count) {
                        memmove(s->ptr+off+len, s->ptr+off+len-no, s->count-off-no);
                    }
                    memcpy(s->ptr+off, str, len);
                    s->count += len - no;
                    return s->ptr;
                }
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC char* ms_string_replace_c(ms_string s, size_t off, size_t no, size_t count, char c) {
#ifdef _IF_NULL_
    if (s && count > 0) {
#endif
    if (off <= s->count) {
        if (off + no > s->count) {
            no = s->count - off;
        }
        if (0 == no) return ms_string_insert_c(s, off, count, c);

        if (no == count) {
            memset(s->ptr+off, c, count);
            return s->ptr;
        }
        else if (no > count) {
            memset(s->ptr+off, c, count);
            if (off + no < s->count)
                memmove(s->ptr+off+count, s->ptr+off+no, s->count-off-no);
            else
                memset(s->ptr+off+count, 0, s->count - off - count);
            s->count -= no - count;
            return s->ptr;
        }
        else {
            if (s->max_count < s->count + count - no + 1) {
                ms_string_grow(s, s->count + count - no + 1);
            }
            if (s->count + count - no < s->max_count) {
                if (off + no < s->count) {
                    memmove(s->ptr+off+count, s->ptr+off+count-no, s->count-off-no);
                }
                memset(s->ptr+off, c, count);
                s->count += count - no;
                return s->ptr;
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC size_t ms_string_find(ms_string s, size_t off, char c) {
    size_t pos = msc_size_npos;
#ifdef _IF_NULL_
    if (s) {
#endif
    if (off < s->count) {
        for (size_t i=off;i<s->count;++i) {
            if (c == s->ptr[i]) {
                pos = i;
                break;
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return pos;
}

CPUBLIC size_t ms_string_find_str(ms_string s, size_t off, char* str, size_t len) {
    size_t pos = msc_size_npos;
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (0 == len) {
        len = strlen(str);
    }
    if (len > 0 && off < s->count && off+len <= s->count) {
        for (size_t i=off;i+len<=s->count;++i) {
            if (memcmp(s->ptr+i, str, len) == 0) {
                pos = i;
                break;
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return pos;
}

CPUBLIC size_t ms_string_rfind(ms_string s, size_t off, char c) {
    size_t pos = msc_size_npos;
#ifdef _IF_NULL_
    if (s) {
#endif
    if (off >= s->count) off = s->count;
    else ++off;
    for (size_t i=off;i>0;--i) {
        if (c == s->ptr[i-1]) {
            pos = i-1;
            break;
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return pos;
}

CPUBLIC size_t ms_string_rfind_str(ms_string s, size_t off, char* str, size_t len) {
    size_t pos = msc_size_npos;
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (0 == len) {
        len = strlen(str);
    }
    if (len > 0 && len <= s->count) {
        if (off >= s->count) off = s->count-len;
        else ++off;
        if (off + len - 1 > s->count)
            off = s->count - len + 1;
        for (size_t i=off;i>0;--i) {
            if (memcmp(s->ptr+i-1, str, len) == 0) {
                pos = i;
                break;
            }
        }
    }
#ifdef _IF_NULL_
    }
#endif
    return pos;
}

CPUBLIC ms_string ms_string_sub(ms_string s, size_t off, size_t count) {
#ifdef _IF_NULL_
    if (s && count > 0) {
#endif
    if (off < s->count) {
        if (off + count > s->count)
            count = s->count - off;
        if (count > 0)
            return ms_string_create(s->ac, s->step, s->ptr+off, count);
    }
#ifdef _IF_NULL_
    }
#endif
    return NULL;
}

CPUBLIC size_t ms_string_reserve(ms_string s, size_t count) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (s->max_count < count) {
        ms_string_reallocate(s,count);
    }
    return s->max_count;
#ifdef _IF_NULL_
    }
    return 0;
#endif
}

CPUBLIC size_t ms_string_resize(ms_string s, size_t count) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (s->max_count == count + 1) {
        s->count = count;
    }
    else {
        ms_string_reallocate(s,count + 1);
        s->count = count;
        s->ptr[count]=0;
    }
    return s->count;
#ifdef _IF_NULL_
    }
    return 0;
#endif
}

CPUBLIC bool ms_string_push_back(ms_string s, char c) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (s->max_count == s->count + 1) {
        ms_string_grow(s, 0);
    }
    if (s->count < s->max_count) {
        //赋值
        s->ptr[s->count] = c;
        ++s->count;
        return true;
    }
#ifdef _IF_NULL_
    }
#endif
    return false;
}

CPUBLIC void ms_string_pop_back(ms_string s) {
    if (!ms_string_empty(s)) {
        --s->count;
        s->ptr[s->count] = 0;
        ms_string_shrink(s);
    }
}

CPUBLIC char ms_string_front(ms_string s) {
#ifdef _IF_NULL_
    if (!ms_string_empty(s)) {
#endif
    return *(s->ptr);
#ifdef _IF_NULL_
    }
    return 0;
#endif
}

CPUBLIC char ms_string_back(ms_string s) {
#ifdef _IF_NULL_
    if (!ms_string_empty(s)) {
#endif
    return *(s->ptr+s->count-1);
#ifdef _IF_NULL_
    }
    return 0;
#endif
}

CPUBLIC void ms_string_remove(ms_string s, size_t pos, size_t len) {
#ifdef _IF_NULL_
    if (ms_string_size(s) && len > 0) {
#endif
    if (pos < s->count) {
        if (pos + len >= s->count) {
            memset(s->ptr+pos, 0, s->count - pos);
            s->count = pos;
        }
        else {
            char* p = s->ptr+pos;
            memmove(p, p+len,s->count-len-pos);
            s->count -= len;
            memset(s->ptr+s->count, 0, len);
        }
        ms_string_shrink(s);
    }
#ifdef _IF_NULL_
    }
#endif
}

CPUBLIC char ms_string_at(ms_string s,size_t pos) {
#ifdef _IF_NULL_
    if (s) {
#endif
    if (pos < s->count) {
        return *(s->ptr + pos);
    }
#ifdef _IF_NULL_
    }
#endif
    return 0;
}

CPUBLIC int ms_string_cmp(ms_string s, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (0 == len) return strcmp(s->ptr, str);
    size_t min_size = s->count > len ? len : s->count;
    int r = memcmp(s->ptr, str, min_size);
    if (r) return r;
    if (s->count < len) return -1;
    if (s->count > len) return 1;
    return 0;
#ifdef _IF_NULL_
    }
    else {
        if (s && s->count) return 1;
        if (str) return -1;
    }
    return 0;
#endif
}

CPUBLIC int ms_string_compare(ms_string left, ms_string right) {
#ifdef _IF_NULL_
    if (left && right) {
#endif
    return ms_string_cmp(left, right->ptr, right->count);
#ifdef _IF_NULL_
    }
    else {
        if (left) return 1;
        if (right) return -1;
    }
    return 0;
#endif
}

CPUBLIC int ms_string_case_cmp(ms_string s, char* str, size_t len) {
#ifdef _IF_NULL_
    if (s && str) {
#endif
    if (0 == len) {
#ifdef _WIN32
        return stricmp(s->ptr, str);
#else
        return strcasecmp(s->ptr, str);
#endif
    }
    size_t min_size = s->count > len ? len : s->count;
#ifdef _WIN32
    int r = strnicmp(s->ptr, str, min_size);
#else
    int r = strncasecmp(s->ptr, str, min_size);
#endif
    if (r) return r;
    if (s->count < len) return -1;
    if (s->count > len) return 1;
    return 0;
#ifdef _IF_NULL_
    }
    else {
        if (s && s->count) return 1;
        if (str) return -1;
    }
    return 0;
#endif
}

CPUBLIC int ms_string_case_compare(ms_string left, ms_string right) {
#ifdef _IF_NULL_
    if (left && right) {
#endif
    return ms_string_case_cmp(left, right->ptr, right->count);
#ifdef _IF_NULL_
    }
    else {
        if (left) return 1;
        if (right) return -1;
    }
    return 0;
#endif
}


CPUBLIC char* ms_string_data(ms_string s) {
#ifdef _IF_NULL_
    if (s) {
#endif
    return s->ptr;
#ifdef _IF_NULL_
    }
    return NULL;
#endif
}


#endif // MSC_STR_H
